home *** CD-ROM | disk | FTP | other *** search
/ Clickx 115 / Clickx 115.iso / software / tools / windows / tails-i386-0.16.iso / live / filesystem.squashfs / usr / sbin / monkeysphere-host < prev    next >
Encoding:
Text File  |  2011-03-24  |  12.5 KB  |  441 lines

  1. #!/usr/bin/env bash
  2.  
  3. # monkeysphere-host: Monkeysphere host admin tool
  4. #
  5. # The monkeysphere scripts are written by:
  6. # Jameson Rollins <jrollins@finestructure.net>
  7. # Jamie McClelland <jm@mayfirst.org>
  8. # Daniel Kahn Gillmor <dkg@fifthhorseman.net>
  9. # Micah Anderson <micah@riseup.net>
  10. #
  11. # They are Copyright 2008-2010, and are all released under the GPL,
  12. # version 3 or later.
  13.  
  14. ########################################################################
  15. set -e
  16.  
  17. # set the pipefail option so pipelines fail on first command failure
  18. set -o pipefail
  19.  
  20. PGRM=$(basename $0)
  21.  
  22. SYSSHAREDIR=${MONKEYSPHERE_SYSSHAREDIR:-"/usr/share/monkeysphere"}
  23. export SYSSHAREDIR
  24. . "${SYSSHAREDIR}/defaultenv"
  25. . "${SYSSHAREDIR}/common"
  26.  
  27. # sharedir for host functions
  28. MHSHAREDIR="${SYSSHAREDIR}/mh"
  29.  
  30. # datadir for host functions
  31. MHDATADIR="${SYSDATADIR}/host"
  32.  
  33. # host pub key files
  34. HOST_KEY_FILE="${SYSDATADIR}/host_keys.pub.pgp"
  35.  
  36. # UTC date in ISO 8601 format if needed
  37. DATE=$(date -u '+%FT%T')
  38.  
  39. # unset some environment variables that could screw things up
  40. unset GREP_OPTIONS
  41.  
  42. ########################################################################
  43. # FUNCTIONS
  44. ########################################################################
  45.  
  46. usage() {
  47.     cat <<EOF >&2
  48. usage: $PGRM <subcommand> [options] [args]
  49. Monkeysphere host admin tool.
  50.  
  51. subcommands:
  52.  import-key (i) FILE SERVICENAME       import PEM-encoded key from file
  53.  show-keys (s) [KEYID ...]             output host key information
  54.  publish-keys (p) [KEYID ...]          publish key(s) to keyserver
  55.  set-expire (e) EXPIRE [KEYID]         set key expiration
  56.  add-servicename (n+) SERVICENAME [KEYID]
  57.                                        add a service name to key
  58.  revoke-servicename (n-) SERVICENAME [KEYID]
  59.                                        revoke a service name from key
  60.  add-revoker (r+) REVOKER_KEYID|FILE [KEYID]
  61.                                        add a revoker to key
  62.  revoke-key [KEYID]                    generate and/or publish revocation
  63.                                        certificate for key
  64.  
  65.  version (v)                           show version number
  66.  help (h,?)                            this help
  67.  
  68. See ${PGRM}(8) for more info.
  69. EOF
  70. }
  71.  
  72. # function to interact with the gpg keyring
  73. gpg_host() {
  74.     GNUPGHOME="$GNUPGHOME_HOST" LC_ALL=C gpg --no-auto-check-trustdb --trust-model=always --no-greeting --quiet --no-tty --fixed-list-mode "$@"
  75. }
  76.  
  77. # list the info about the a key, in colon format, to stdout
  78. gpg_host_list_keys() {
  79.     if [ "$1" ] ; then
  80.     gpg_host --list-keys --with-colons \
  81.         --with-fingerprint --with-fingerprint \
  82.         "$1"
  83.     else
  84.     gpg_host --list-keys --with-colons \
  85.         --with-fingerprint --with-fingerprint
  86.     fi
  87. }
  88.  
  89. # edit key scripts, takes scripts on stdin, and keyID as first input
  90. gpg_host_edit() {
  91.     gpg_host --command-fd 0 --edit-key "$@"
  92. }
  93.  
  94. # export the monkeysphere OpenPGP pub key file
  95. update_pgp_pub_file() {
  96.     log debug "updating openpgp public key file '$HOST_KEY_FILE'..."
  97.     gpg_host --export --armor --export-options export-minimal \
  98.         $(gpg_host --list-secret-keys --with-colons --fingerprint | grep ^fpr | cut -f10 -d:) \
  99.         > "$HOST_KEY_FILE"
  100. }
  101.  
  102. # check that the service name is well formed. we assume that the
  103. # service name refers to a host; DNS labels for host names are limited
  104. # to a very small range of characters (see RFC 1912, section 2.1).
  105.  
  106. # FIXME: i'm failing to check here for label components that are
  107. # all-number (e.g. ssh://666.666), which are technically not allowed
  108. # (though some exist on the 'net, apparently)
  109.  
  110. # FIXME: this will probably misbehave if raw IP addresses are provided,
  111. # either IPv4 or IPv6 using the bracket notation.
  112.  
  113. # FIXME: this doesn't address the use of hashed User IDs.
  114.  
  115. check_service_name() {
  116.     local name="$1"
  117.     local errs=""
  118.     local scheme
  119.     local port
  120.     local assigned_ports
  121.  
  122.     [ -n "$name" ] || \
  123.         failure "You must supply a service name to check"
  124.  
  125.     printf '%s' "$name" | perl -n -e '($str = $_) =~ s/\s//g ; exit !(lc($str) eq $_);' || \
  126.         failure "Not a valid service name: '$name'
  127.  
  128. Service names should be canonicalized to all lower-case,
  129. with no whitespace"
  130.  
  131.     [[ "$name" =~ ^[a-z0-9./:-]+$ ]] || \
  132.         failure "Not a valid service name: '$name'
  133.  
  134. Service names should contain only lower-case ASCII letters
  135. numbers, dots (.), hyphens (-), slashes (/), and a colon (:).
  136. If you are using non-ASCII characters (e.g. IDN), you should
  137. use the canonicalized ASCII (NAMEPREP -> Punycode) representation
  138. (see RFC 3490)."
  139.  
  140.     [[ "$name" =~ \. ]] || \
  141.         failure "Not a valid service name: '$name'
  142.  
  143. Service names should use fully-qualified domain names (FQDN), but the
  144. domain name you chose appears to only have the local part.  For
  145. example: don't use 'ssh://foo' ; use 'ssh://foo.example.com' instead."
  146.  
  147.     [[ "$name" =~ ^[a-z0-9]([a-z0-9-]*[a-z0-9])?://[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.|((\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+))(:[1-9][0-9]{0,4})?$ ]] || \
  148.         failure "Not a valid service name: '$name'
  149.  
  150. Service names look like <scheme>://full.example.com[:<portnumber>],
  151. where <scheme> is something like ssh or https, and <portnumber> is
  152. a decimal number (supplied only if the service is on a non-standard
  153. port)."
  154.     
  155.     scheme=$(cut -f1 -d: <<<"$name")
  156.     port=$(cut -f3 -d: <<<"$name")
  157.     
  158.     # check that the scheme name is found in the system services
  159.     # database
  160.     available_=$(get_port_for_service "$scheme") || \
  161.         log error "Error looking up service scheme named '%s'" "$scheme"
  162.  
  163.     # FIXME: if the service isn't found, or does not have a port, what
  164.     # should we do? at the moment, we're just warning.
  165.     
  166.     if [ -n "$port" ]; then
  167.     # check that the port number is a legitimate port number (> 0, < 65536)
  168.         [ "$port" -gt 0 ] && [ "$port" -lt 65536 ] || \
  169.             failure "The given port number should be greater than 0 and
  170. less than 65536.  '$port' is not OK"
  171.  
  172.     # if the port number is given, and the scheme is in the services
  173.     # database, check that the port number does *not* match the
  174.     # default port.
  175.         if (printf '%s' "$assigned_ports" | grep -q -F -x "$port" ) ; then
  176.             failure $(printf "The scheme %s uses port number %d by default.
  177. You should leave off the port number if it is the default" "$scheme" "$port")
  178.         fi
  179.     fi
  180.  
  181. }
  182.  
  183. # fail if host key not present
  184. check_no_keys() {
  185.     [ -s "$HOST_KEY_FILE" ] \
  186.     || failure "You don't appear to have a Monkeysphere host key on this server.
  187. Please run 'monkeysphere-host import-key' import a key."
  188. }
  189.  
  190. # key input to functions, outputs full fingerprint of specified key if
  191. # found
  192. check_key_input() {
  193.     local keyID="$1"
  194.     # array of fingerprints
  195.     local fprs=($(list_primary_fingerprints <"$HOST_KEY_FILE"))
  196.  
  197.     case ${#fprs[@]} in
  198.     0)
  199.         failure "You don't appear to have any Monkeysphere host keys.
  200. Please run 'monkeysphere-host import-key' to import a key."
  201.         ;;
  202.     1)
  203.         :
  204.         ;;
  205.     *)
  206.         if [ -z "$keyID" ] ; then
  207.         failure "Your host keyring contains multiple keys.
  208. Please specify one to act on (see 'monkeysphere-host show-keys')."
  209.         fi
  210.         ;;
  211.     esac
  212.     printf '%s\n' "${fprs[@]}" | grep "${keyID}$" \
  213.     || failure "Host key '$keyID' not found."
  214. }
  215.  
  216. # return 0 if user ID was found.
  217. # return 1 if user ID not found.
  218. check_key_userid() {
  219.     local keyID="$1"
  220.     local userID="$2"
  221.     local tmpuidMatch
  222.  
  223.     # match to only "unknown" user IDs (host has no need for ultimate trust)
  224.     tmpuidMatch="uid:-:$(echo $userID | gpg_escape)"
  225.  
  226.     # See whether the requsted user ID is present
  227.     gpg_host_list_keys "$keyID" | cut -f1,2,10 -d: | \
  228.     grep -q -x -F "$tmpuidMatch" 2>/dev/null
  229. }
  230.  
  231. prompt_userid_exists() {
  232.     local userID="$1"
  233.     local gpgOut
  234.     local fingerprint
  235.  
  236.     if gpgOut=$(gpg_host_list_keys "=${userID}" 2>/dev/null) ; then
  237.     fingerprint=$(echo "$gpgOut" | grep '^fpr:' | cut -d: -f10)
  238.     if [ "$PROMPT" != "false" ] ; then
  239.         printf "Service name '%s' is already being used by key '%s'.\nAre you sure you want to use it again? (y/N) " "$userID" "$fingerprint" >&2
  240.         read OK; OK=${OK:=N}
  241.         if [ "${OK/y/Y}" != 'Y' ] ; then
  242.         failure "Service name not added."
  243.         fi
  244.     else
  245.         log info "Key '%s' is already using the service name '%s'." "$fingerprint" "$userID" >&2
  246.     fi
  247.     fi
  248. }
  249.  
  250. # run command looped over keys
  251. multi_key() {
  252.     local cmd="$1"
  253.     shift
  254.     local keys=$@
  255.     local i=0
  256.     local key
  257.  
  258.     check_no_keys
  259.  
  260.     log debug "listing primary fingerprints from $HOST_KEY_FILE"
  261.     local fprs=($(list_primary_fingerprints <"$HOST_KEY_FILE"))
  262.     log debug "obtained the following fingerprints: $fprs"
  263.  
  264.     if [[ -z "$1" || "$1" == '--all' ]] ; then
  265.     log debug "publishing all keys"
  266.     keys="${fprs[@]}"
  267.     fi
  268.  
  269.     log debug "using keys: $keys"
  270.  
  271.     for key in $keys ; do
  272.     if (( i++ > 0 )) ; then
  273.         printf "\n"
  274.     fi
  275.     log debug "invoking $cmd $key"
  276.     "$cmd" "$key"
  277.     done
  278. }
  279.  
  280. # show info about the a key
  281. show_key() {
  282.     local id="$1"
  283.     local GNUPGHOME
  284.     local fingerprint
  285.     local revokers
  286.  
  287.     # tmp gpghome dir
  288.     export GNUPGHOME=$(msmktempdir)
  289.  
  290.     # trap to remove tmp dir if break
  291.     trap "rm -rf $GNUPGHOME" EXIT
  292.  
  293.     # import the host key into the tmp dir
  294.     gpg --quiet --import <"$HOST_KEY_FILE"
  295.  
  296.     # get the gpg fingerprint
  297.     if gpg --quiet --list-keys \
  298.     --with-colons --with-fingerprint "$id" \
  299.     | grep '^fpr:' | cut -d: -f10 > "$GNUPGHOME"/fingerprint ; then
  300.     fingerprint=$(cat "$GNUPGHOME"/fingerprint)
  301.     else
  302.     failure "ID '$id' not found."
  303.     fi
  304.  
  305.     # list the host key info
  306.     # FIXME: make no-show-keyring work so we don't have to do the grep'ing
  307.     # FIXME: can we show uid validity somehow?
  308.     gpg --list-keys --list-options show-unusable-uids "$fingerprint" 2>/dev/null \
  309.         | grep -v "^${GNUPGHOME}/pubring.gpg$" \
  310.         | egrep -v '^-+$' \
  311.         | grep -v '^$'
  312.  
  313.     # list revokers, if there are any
  314.     revokers=$(gpg --list-keys --with-colons --fixed-list-mode "$fingerprint" \
  315.     | awk -F: '/^rvk:/{ print $10 }' )
  316.     if [ "$revokers" ] ; then
  317.     echo "The following keys are allowed to revoke this host key:"
  318.     for key in $revokers ; do
  319.         echo "revoker: $key"
  320.     done
  321.     fi
  322.  
  323.     # list the pgp fingerprint
  324.     echo "OpenPGP fingerprint: $fingerprint"
  325.  
  326.     # list the ssh fingerprint
  327.     printf "ssh fingerprint: %s\n" \
  328.     "$(gpg --export --no-armor "$fingerprint" 2>/dev/null | "$SYSSHAREDIR/keytrans" openpgp2sshfpr "$fingerprint")"
  329.  
  330.     # remove the tmp file
  331.     trap - EXIT
  332.     rm -rf "$GNUPGHOME"
  333. }
  334.  
  335. ########################################################################
  336. # MAIN
  337. ########################################################################
  338.  
  339. # load configuration file
  340. [ -e ${MONKEYSPHERE_HOST_CONFIG:="${SYSCONFIGDIR}/monkeysphere-host.conf"} ] \
  341.     && . "$MONKEYSPHERE_HOST_CONFIG"
  342.  
  343. # set empty config variable with ones from the environment, or with
  344. # defaults
  345. LOG_LEVEL=${MONKEYSPHERE_LOG_LEVEL:=$LOG_LEVEL}
  346. KEYSERVER=${MONKEYSPHERE_KEYSERVER:=$KEYSERVER}
  347. log debug "using keyserver: $KEYSERVER"
  348. CHECK_KEYSERVER=${MONKEYSPHERE_CHECK_KEYSERVER:=$CHECK_KEYSERVER}
  349. MONKEYSPHERE_USER=${MONKEYSPHERE_MONKEYSPHERE_USER:=$MONKEYSPHERE_USER}
  350. MONKEYSPHERE_GROUP=$(get_primary_group "$MONKEYSPHERE_USER")
  351. PROMPT=${MONKEYSPHERE_PROMPT:=$PROMPT}
  352.  
  353. # other variables
  354. GNUPGHOME_HOST=${MONKEYSPHERE_GNUPGHOME_HOST:="${MHDATADIR}"}
  355. LOG_PREFIX=${MONKEYSPHERE_LOG_PREFIX:='ms: '}
  356.  
  357. # export variables needed in su invocation
  358. export DATE
  359. export LOG_LEVEL
  360. export KEYSERVER
  361. export CHECK_KEYSERVER
  362. export MONKEYSPHERE_USER
  363. export MONKEYSPHERE_GROUP
  364. export PROMPT
  365. export GNUPGHOME_HOST
  366. export GNUPGHOME
  367. export HOST_FINGERPRINT
  368. export LOG_PREFIX
  369.  
  370. if [ "$#" -eq 0 ] ; then 
  371.     usage
  372.     failure "Please supply a subcommand."
  373. fi
  374.  
  375. # get subcommand
  376. COMMAND="$1"
  377. shift
  378.  
  379. case $COMMAND in
  380.     'import-key'|'import'|'i')
  381.     source "${MHSHAREDIR}/import_key"
  382.     import_key "$@"
  383.     ;;
  384.  
  385.     'show-keys'|'show-key'|'show'|'s')
  386.     multi_key show_key "$@"
  387.     ;;
  388.  
  389.     'set-expire'|'extend-key'|'extend'|'e')
  390.     source "${MHSHAREDIR}/set_expire"
  391.     set_expire "$@"
  392.     ;;
  393.  
  394.     'add-servicename'|'add-hostname'|'add-name'|'n+')
  395.     source "${MHSHAREDIR}/add_name"
  396.     add_name "$@"
  397.     ;;
  398.  
  399.     'revoke-servicename'|'revoke-hostname'|'revoke-name'|'n-')
  400.     source "${MHSHAREDIR}/revoke_name"
  401.     revoke_name "$@"
  402.     ;;
  403.  
  404.     'add-revoker'|'r+')
  405.     source "${MHSHAREDIR}/add_revoker"
  406.     add_revoker "$@"
  407.     ;;
  408.  
  409.     'revoke-key')
  410.     source "${MHSHAREDIR}/revoke_key"
  411.     revoke_key "$@"
  412.     ;;
  413.  
  414.     'publish-keys'|'publish-key'|'publish'|'p')
  415.     source "${MHSHAREDIR}/publish_key"
  416.     multi_key publish_key "$@"
  417.     ;;
  418.  
  419.     'diagnostics'|'d')
  420.     source "${MHSHAREDIR}/diagnostics"
  421.     diagnostics
  422.     ;;
  423.  
  424.     'update-pgp-pub-file')
  425.     update_pgp_pub_file
  426.     ;;
  427.  
  428.     'version'|'--version'|'v')
  429.     version
  430.     ;;
  431.  
  432.     '--help'|'help'|'-h'|'h'|'?')
  433.         usage
  434.         ;;
  435.  
  436.     *)
  437.         failure "Unknown command: '$COMMAND'
  438. Try '$PGRM help' for usage."
  439.         ;;
  440. esac
  441.